package socketmvc.core.processor;

import socketmvc.core.exception.AmbiguousProcessorMethodCommandException;
import socketmvc.core.exception.ProcessorMustNonNullException;
import socketmvc.core.util.ClassScanUtils;
import socketmvc.core.processor.annotation.SocketCommand;
import socketmvc.core.processor.annotation.SocketControl;
import socketmvc.core.processor.bean.Processor;
import socketmvc.core.processor.bean.ProcessorMethod;
import java.lang.reflect.Method;
import java.nio.file.Path;
import java.util.*;

/**
 * 处理器方法初始化
 * 这是整个xim的核心处理类
 * @author xueyanjun
 */
public class ProcessorMethodHandler {

    private final Map<String, ProcessorMethod> PROCESSOR_METHOD_MAP = new HashMap<>();
    private final boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows");

    public void init(Collection<String> packages) throws Exception {

        // 扫描获取包下的所有被@ImProcessor注解标注的类
        List<Class<?>> classes = scanProcessorClass(packages);
        for (Class<?> cls : classes) {
            // 创建process(用户可重写生成适合运行环境的process)
            Processor<?> processor = createProcessor(cls);
            if (processor == null){
                throw new ProcessorMustNonNullException();
            }
            Method[] methods = cls.getMethods();
            for (Method method : methods) {
                // 创建processMethod(用户可重写生成适合运行环境的processMethod)
                ProcessorMethod processorMethod = createProcessorMethod(processor, method);
                if (processorMethod != null) {
                    storeProcessorMethod(processorMethod);
                }
            }
        }

    }

    /**
     * 扫描process业务处理器
     * @param packages 包名
     * @return class
     * @throws Exception ex
     */
    protected List<Class<?>> scanProcessorClass(Collection<String> packages) throws Exception{
        List<Class<?>> classes = new LinkedList<>();
        for (String aPackage : packages) {
            List<Class<?>> pcs = ClassScanUtils.getClassListByAnnotation(aPackage, SocketControl.class);
            classes.addAll(pcs);
        }
        return classes;
    }

    /**
     * 创建processor业务处理器对象
     * 扫描完成后会依次调用该函数实现创建
     * @param cls process class
     * @param <T> process type
     * @return 业务处理器对象
     * @throws Exception ex
     */
    protected <T> Processor<T> createProcessor(Class<T> cls) throws Exception {
        SocketControl processorAnnotation = cls.getAnnotation(SocketControl.class);
        SocketCommand commandAnnotation = cls.getAnnotation(SocketCommand.class);
        Processor<T> processor = new Processor<>(cls);
        if (processorAnnotation != null) {
            String cmd = processorAnnotation.value();
            processor.setCommand(cmd);
        }else {
            processor.setCommand("/");
        }
        if (commandAnnotation != null){
            String cmd = commandAnnotation.value();
            String old = processor.getCommand();
            processor.setCommand(Path.of(old,cmd).toString());
        }
        return processor;
    }

    /**
     * 创建处理器函数
     * createProcessor 函数调用完成后，会依次调用该函数创建处理器内部方法
     * @param processor 处理器
     * @param method method
     * @return 处理器内部方法
     * @throws Exception ex
     */
    protected ProcessorMethod createProcessorMethod(Processor<?> processor, Method method) throws Exception {
        SocketCommand commandAnnotation = method.getAnnotation(SocketCommand.class);
        if (commandAnnotation != null) {
            String methodCmd = commandAnnotation.value();
            boolean methodCmdIsRoot = commandAnnotation.root();
            if (!methodCmdIsRoot){
                String rootCmd = Optional.ofNullable(processor.getCommand()).orElse("/");
                if (!rootCmd.startsWith("/@")){
                    methodCmd = Path.of("/##",rootCmd, methodCmd).normalize().toString();
                }else {
                    methodCmd = rootCmd + methodCmd;
                }
            }else {
                methodCmd = Path.of("/##", methodCmd).normalize().toString();
            }
            if(isWindows){
                methodCmd = methodCmd.replace("\\", "/");
            }
            methodCmd = methodCmd.replace("/##","");
            return new ProcessorMethod(methodCmd, processor, method);
        }
        return null;
    }


    /**
     * 保存processor 业务处理器
     * @param method me
     */
    public void storeProcessorMethod(ProcessorMethod method) {
        String cmd = method.getCommand();
        if (!cmd.startsWith("/@")) {
            // 非内置处理器
            ProcessorMethod pre = PROCESSOR_METHOD_MAP.put(cmd, method);
            if (pre != null) {
                throw new AmbiguousProcessorMethodCommandException(String.format("存在模糊的命令码=> [cmd: %s , method1: %s , method2: %s]",cmd,pre.getName(),method.getName()));
            }
        } else {
            // 以 /@ 开头的命令码为框架内置的处理器，可以进行覆盖
            PROCESSOR_METHOD_MAP.put(cmd, method);
        }
    }


    /**
     * 通过命令码获取处理器函数
     * @param cmd 命令码
     * @return 处理器函数
     */
    public ProcessorMethod getProcessorMethod(String cmd) {
        return PROCESSOR_METHOD_MAP.get(cmd);
    }

}
